package server.game;

import server.controller.OutboundMessages;
import server.util.Logger;
import shared.messages.CoordMessage;
import shared.messages.WinMessage;
import shared.player.Player;
import shared.util.Config;

/**
 * The Class GameController
 *  - controls the game.
 */
public class GameController extends Thread {
	
	/** The outboundMessages object. */
	OutboundMessages outQueue;
	
	/** The games id number. */
	int gameId;
	
	/** The players in the game. */
	Player p1, p2, p3;
	
	/** The wait before move is the time the thread sleeps before trying another player move. */
	int waitBeforeMove;
	
	/** The gameboard. */
	Integer[][] board;
	
	/** A boolean to show if the game has ended. */
	GameEndChecker check;
	
	/** The configuration object. */
	Config config;
	
	/** The logger for logging server messages and errors. */
	Logger logger;
	
	/** A boolean indicating whether the game should start (true) or has ended (false). */
	private boolean game = true;
	
	/** Winner of the game, null if tie */
	private Player winner;

	/**
	 * Instantiates a new game controller.
	 *
	 * @param outQueue the outbound messages object
	 * @param gameId the games id number
	 * @param p1 the player1
	 * @param p2 the player2
	 * @param p3 the player3
	 * @param board the gameboard
	 * @param logger the logger for server messages and errors
	 * @param config the configuration object
	 */
	public GameController(OutboundMessages outQueue, int gameId, Player p1, Player p2,
			Player p3, Integer[][] board, Logger logger, Config config) {
		this.outQueue = outQueue;
		this.board = board;
		this.logger = logger;
		this.gameId = gameId;
		this.p1 = p1;
		this.p2 = p2;
		this.p3 = p3;
		this.config = config;
		start();
	}

	/** moves players and sends coordinate changes to clients,
	 * creates a new game end checker
	 * @see java.lang.Thread#run()
	 */
	public void run() {
		check = new GameEndChecker(p1, p2, p3, board, this);
		waitBeforeMove = config.getWaitBeforeMove();
		while (game) {
			if (checkMoveReady()) {
				try {
					Thread.sleep(waitBeforeMove);
				} catch (InterruptedException e) {
					logger.logError("Game - run() - sleep interrupted.");
				}
				if (check.aliveP1) {
					p1.move(board);
				}
				if (check.aliveP2) {
					p2.move(board);
				}
				if(check.aliveP3){
					p3.move(board);
				}
				check.check();
				outQueue.addMessage(ComposeCoordMessage());
			}
		}
		String gameRes = "Game " + gameId + " ended. ";
		if(winner == null){
			gameRes += "Tie game.";
		}else{
			gameRes += "Winner: " + winner.name;
		}
		logger.log(gameRes);
		outQueue.addMessage(composeWinMessage());
	}

	/**
	 * Compose win message.
	 *
	 * @return the win message
	 */
	private WinMessage composeWinMessage() {
		return new WinMessage(winner, gameId);
	}

	/**
	 * Check if players are ready for a new move.
	 *
	 * @return true, if successful
	 */
	private boolean checkMoveReady() {
		if (p1.readyForMove && p2.readyForMove && p3.readyForMove) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Compose coordinate message.
	 *
	 * @return the coord message
	 */
	private CoordMessage ComposeCoordMessage() {
		return new CoordMessage(gameId, encodeIdXY(p1.playerId, p1.x, p1.y),
				encodeIdXY(p2.playerId, p2.x, p2.y), encodeIdXY(p3.playerId, p3.x, p3.y));
	}

	/**
	 * Encode id, x, y.
	 *
	 * @param id the player id
	 * @param x the x coordinate
	 * @param y the y coordinate
	 * @return the int coded result
	 */
	public int encodeIdXY(int id, int x, int y) {
		return id * 1000000 + x * 1000 + y;
	}
	
	/**
	 * Sets the game false, ending the game.
	 */
	public void endGame(){
		game = false;
	}
	
	/**
	 * Sets the winner.
	 *
	 * @param p the winner
	 */
	public void setWinner(Player p){
		winner = p;
	}
}
